#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <sstream>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <wait.h>
#include "Until.hpp"
#include "Log.hpp"

//状态码
#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500


#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"

#define HTTP_VERSION "HTTP/1.0"
#define END_LINE "\r\n"

//根据响应状态码拿到响应状态码描述
static std::string Code2Desc(int code)
{
    std::string desc;
    switch(code)
    {
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "Not Found";
            break;
        default:
            break;
    }
    return desc;
}

//根据文件类型确定Content-Type的文件扩展名
//suffix: .html .css .js等 加.的后缀
static std::string Suffix2Desc(const std::string& suffix)
{
    std::unordered_map<std::string,std::string> suffix2desc = {
        {".html","text/html"},
        {".css","text/css"},
        {".js","application/javascript"},
        {".jgp","application/x-jpg"},
        {".xml","application/xml"}
    };
    auto iter = suffix2desc.find(suffix);
    if(iter != suffix2desc.end())
    {
        return iter->second;
    }
    return "text/html";
}

//http请求结构体
class HttpRequest{
    public:
        std::string request_line; //请求首行
        std::vector<std::string> request_header; //请求头部
        std::string blank; //空行
        std::string request_body; //正文
        std::string method; //请求方法
        std::string uri; //资源标识符
        std::string version; //协议版本
        std::unordered_map<std::string,std::string> header_kv; //以键值对的形式保存头部信息
        int content_length; //正文长度
        std::string path; //url
        std::string query_string; //查询字符串
        bool cgi;  //用来标记是否为cgi请求
        std::string suffix; //文件后缀
    public:
        HttpRequest()
            :content_length(0)
            ,cgi(false)
        {}
        ~HttpRequest(){}
};

//Http响应结构体
class HttpResponse{
    public:
        std::string status_line; //响应首行
        std::vector<std::string> response_header; //响应头部信息
        std::string blank; //空行
        std::string response_body; //响应正文
        int status_code; //响应状态码
        int fd; //响应资源对应的文件描述符
        int size; //响应资源大小
    public:
        HttpResponse()
            :blank(END_LINE)
            ,status_code(OK)
            ,fd(-1)
        {}
        ~HttpResponse(){}
};

//读取请求,分析请求,构建响应
//IO通信
class EndPoint{
    private:
        int sock; //套接字描述符
        HttpRequest http_request; //Http请求信息
        HttpResponse http_response; //Http响应信息
        bool stop;
    private:
        //读取Http请求的首行信息至 request_line
        bool RecvHttpRequestLine()
        {
            auto& line = http_request.request_line;
            if(Until::ReadLine(sock,line) > 0)
            {
                line.resize(line.size()-1);
                LOG(INFO,line);
            }
            else
            {
                stop = true;
            }
            std::cout << "RecvHttpRequestLine: " << stop << std::endl;
            return stop;
        }
        //接收Http请求的头部信息以及空行至 request_header 和 blank 
        bool RecvHttpRequestHeader()
        {
            std::string line;
            while(true)
            {
                line.clear();
                if(Until::ReadLine(sock,line) <= 0)
                {
                    stop = true;
                    break;
                }
                
                if(line == "\n")
                {
                    http_request.blank = line;
                    break;
                }
                line.resize(line.size()-1);
                http_request.request_header.push_back(line);
                LOG(INFO,line);
            }
            std::cout << "RecvHttpRequestHeader: " << stop << std::endl;
            return stop;
        }
        //解析请求首行至 method(请求方法) uri(资源标识符) version(协议版本)
        void ParseHttpRequestLine()
        {
            auto& line = http_request.request_line;
            std::stringstream ss(line);
            //利用stringstream对象从请求首行获取请求方法,uri以及协议版本
            ss >> http_request.method >> http_request.uri >> http_request.version;
            //利用transform方法 将请求方法统一写成大写字符
            auto& method = http_request.method;
            std::transform(method.begin(),method.end(),method.begin(),::toupper);
        }
        //解析请求头部 至 header_kv内
        void ParseHttpRequestHeader()
        {
            std::string key;
            std::string value;
            for(auto& iter : http_request.request_header)
            {
                std::string SEP = ": ";
                if(Until::CutString(iter,key,value,SEP))
                {
                    //std::cout << key << std::endl;
                    //std::cout << value << std::endl;
                    http_request.header_kv.insert({key,value});
                }
            }
        }

        //判断是否需要接收请求正文
        bool IsNeedRecvHttpRequestBody()
        {
            auto& method = http_request.method;
            //如果是POST请求方法,则数据都保存在正文中
            if(method == "POST")
            {
                //拿到Content-Length 至 http请求的 content_length 用于读取正文数据
                auto& header_kv = http_request.header_kv;
                auto iter = header_kv.find("Content-Length");
                if(iter != header_kv.end())
                {
                    http_request.content_length = atoi(iter->second.c_str()); 
                    return true;
                }

            }
            return false;
        }
        //读取请求正文数据
        void RecvHttpRequestBody()
        {
            //如果需要读取正文数据
            if(IsNeedRecvHttpRequestBody())
            {
                //获取正文数据长度:Content-Length
                int content_length = http_request.content_length;
                //读取正文数据至 request_body中
                auto& body = http_request.request_body;
                char ch = 0;
                //逐字符读取正文数据
                while(content_length)
                {
                     size_t s = recv(sock,&ch,1,0);
                     if(s > 0)
                     {
                         body.push_back(ch);
                         content_length--;
                     }
                     else
                     {
                         //并不做过多处理,直接break;
                         break;
                     }

                }
            }
        }

        //利用cgi程序处理数据
        int ProcessCgi()
        {
            LOG(INFO,"process cgi method...");

            int code = OK;
            //GET请求方法,数据保存在查询字符串中
            auto& query_string = http_request.query_string;
            //POST请求方法,数据保存在请求正文中
            auto& body_text = http_request.request_body;
            //请求方法
            auto& method = http_request.method;
            std::string query_string_env;
            std::string method_env;
            std::string content_length_env;
            //获取cgi可执行程序路径
            auto& bin = http_request.path;

            std::string& response_body = http_response.response_body;

            int input[2];
            int output[2];
            if(pipe(input) < 0)
            {
                LOG(ERROR,"pipe input error!");
                return NOT_FOUND;
            }
            if(pipe(output) <0)
            {
                LOG(ERROR,"pipe output error!");
                return NOT_FOUND;
            }
            //创建子进程
            pid_t pid = fork();
            if(pid == 0) //子进程
            {
                close(input[0]);
                close(output[1]);

                //重定向
                //站在子进程的角度
                //input[1]: 写出 重定向1->input[1]
                //output[0]: 读入 重定向0->output[0]
                //将请求方法写入到环境变量中,子进程程序替换后通过环境变量获取请求方法,通过请求方法,判别数据的传输方式
                //如果是GET请求方法,则数据保存在查询字符串query_string中,通过环境变量传递给替换后的进程
                //如果是POST请求方法,则数据保存在正文request_body中,通过管道传递给替换后的进程
                method_env = "METHOD=";
                method_env += method;
                putenv((char*)method_env.c_str());
                if(method == "GET")
                {
                    //将query_string的数据写入环境变量中
                    query_string_env = "QUERY_STRING=";
                    query_string_env += query_string;
                    putenv((char*)query_string_env.c_str());
                    LOG(INFO,"Get Mothed,Add Query_String Env");
                }
                else if(method == "POST")
                {
                    //如果是GET请求方法还需要通过环境变量传递正文的大小Content-Length
                    content_length_env = "CONTENT_LENGTH=";
                    content_length_env += std::to_string(http_request.content_length);
                    putenv((char*)content_length_env.c_str());
                    LOG(INFO,"Post Mothed,Add Content_Length Env");
                }
                else
                {
                    //用于项目的扩展
                    //Do Nothing
                }

                //dup2(oldfd,newfd); //将newfd重定向至oldfd
                dup2(input[1],1);
                dup2(output[0],0);
                //程序替换,指向目标可执行程序
                //程序替换成功之后,目标子进程该如何得知管道对应的读写文件描述符是多少呢? 
                //重定向后不需要知道,从0(标准输入)读取,向1(标准输出)写入即可
                execl(bin.c_str(),bin.c_str(),nullptr);
                exit(1);
            }
            else if (pid < 0) //创建子进程出错
            {
                LOG(ERROR,"fork error!");
                return NOT_FOUND;
            }
            else//父进程
            {
                close(input[1]);
                close(output[0]);
                //如果是POST请求方法,则将正文数据通过管道传递给子进程
                if(method == "POST")
                {
                    int size = 0;
                    int total = 0;
                    const char* start = body_text.c_str();
                    while(total < http_request.content_length && (size = write(output[1],start+total,body_text.size()-total)) > 0)
                    {
                        total += size;
                    }
                }

                //从input[0]读取CGI程序的处理数据,读取至response_body中
                char ch = 0;
                while(read(input[0],&ch,1) > 0)
                {
                    response_body.push_back(ch);
                }
                int status = 0;

                pid_t ret = waitpid(pid,&status,0); //等待子进程退出,回收子进程
                if(ret == pid)
                {
                    if(WIFEXITED(status)) //WIFEXITED返回非0值表示子进程正常退出
                    {
                        if(WEXITSTATUS(status) == 0) //WEXITSTATUS返回0,表示正常退出
                        {
                            code = OK;
                        }
                        else
                        {
                            //子进程异常退出
                            code = NOT_FOUND;
                        }
                    }
                    else
                    {
                        //子进程异常退出
                        code = NOT_FOUND;
                    }
                }
                close(input[0]);
                close(output[1]);
            }

            return code;
        }
        //处理非cgi请求
        int ProcessNonCgi()
        {
            //只需要 打开需要响应的文件资源
            http_response.fd = open(http_request.path.c_str(),O_RDONLY);
            if(http_response.fd >= 0)
            {
                return OK;
            }
            return NOT_FOUND;
        }
        //协助组织http响应
        void BuildHttpResponseHelper()
        {
            //引用获取响应状态码
            auto& code = http_response.status_code;
            //组织响应状态首行: 协议版本 响应状态码 响应状态码描述 换行
            auto& status_line = http_response.status_line;
            status_line += HTTP_VERSION;
            status_line += " ";
            status_line += std::to_string(code);
            status_line += " ";
            status_line += Code2Desc(code);
            status_line += END_LINE;
            //构建错误响应所需要返回的资源路径
            std::string path = WEB_ROOT;
            path += "/";
            switch(code)
            {
                case OK:
                    BuildOkRespon();
                    break;
                case NOT_FOUND:
                    //www_root/404.html
                    path += PAGE_404;
                    HeaderError(path);
                    break;
                /*case BAD_REQUEST:
                    path += PAGE_404;
                    HeaderError(path);
                    break;
                case SERVER_ERROR:
                    path += PAGE_404;
                    HeaderError(path);
                    break;*/
                default:
                    break;
            }
        }
        //组织成功响应(响应头部信息)
        void BuildOkRespon()
        {
            //组织响应头部信息: Content-Type
            std::string line = "Content-Type: ";
            line += Suffix2Desc(http_request.suffix);
            line += END_LINE;//空行
            http_response.response_header.push_back(line);
            //组织响应头部信息: Content-Length
            line = "Content-Length: ";
            if(http_request.cgi)
            {
                //响应正文的大小
                line += std::to_string(http_response.response_body.size());
            }
            else
            {
                //资源文件的大小
                line += std::to_string(http_response.size);
            }
            line += END_LINE;//加上空行
            http_response.response_header.push_back(line);
        }
        //组织错误响应(打开404.HTML文件  组织响应头部信息)
        void HeaderError(std::string page)
        {
            std::cout << "Debug: " << page << std::endl;
            //有可能是cgi程序执行出错
            http_request.cgi = false;

            //打开错误响应的文件 404.html进行响应
            http_response.fd = open(page.c_str(),O_RDONLY);

            if(http_response.fd > 0)
            {
                struct stat st;
                //获取文件相关信息
                stat(page.c_str(),&st);
                //将文件的大小 作为 响应正文的大小
                http_response.size = st.st_size;
                //组织Content-Type
                std::string line = "Content-Type: text/html";
                line += END_LINE;
                http_response.response_header.push_back(line);
                //组织Content-Length
                line = "Content-Length: ";
                line += std::to_string(st.st_size);
                line += END_LINE;
                http_response.response_header.push_back(line);

            }
        }
    public:
        EndPoint(int _sock)
            :sock(_sock)
            ,stop(false)
        {}

        bool IsStop()
        {
            return stop;
        }
        //接收请求信息并解析
        void RecvHttpRequest()
        {
            //如果接收首行和头部信息成功,则进行解析信息以及接收正文数据
            if((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
            {
                ParseHttpRequestLine();
                ParseHttpRequestHeader();
                RecvHttpRequestBody();
            }
        }
        //处理http请求并且构建响应信息
        void BuildHttpResponse()
        {
            std::string _path;
            struct stat st;
            size_t found = 0;
            
            auto& code = http_response.status_code;
            if(http_request.method != "GET" && http_request.method != "POST")
            {
                //非法请求
                LOG(WARNING,"method is not right");
                code = NOT_FOUND;
                goto END;
            }

            //GET和POST请求:
            if(http_request.method == "GET")
            {
                size_t pos = http_request.uri.find('?');
                if(pos != std::string::npos) //表示有查询字符串
                {
                    std::string sep = "?";
                    //如果是GET请求方法并且uri中包含?,则表示有查询字符串,则将uri进行切分为 path资源路径 和 query_string查询字符串
                    Until::CutString(http_request.uri,http_request.path,http_request.query_string,sep);
                    http_request.cgi = true;
                }
                else
                {
                    //没有查询字符串
                    http_request.path = http_request.uri;
                }
            }
            else if(http_request.method == "POST")
            {
                //post请求方法,一定是cgi请求
                http_request.cgi = true;
                http_request.path = http_request.uri;

            }

            //资源路径加上web根目录
            _path = http_request.path;
            http_request.path = WEB_ROOT;
            http_request.path += _path;
            //如果path的最后一个字符是'/',则需要给path加上指定的资源路径进行返回 www_root/index.html
            if(http_request.path[http_request.path.size()-1] == '/')
            {
                http_request.path += HOME_PAGE;
            }

            if(stat(http_request.path.c_str(),&st) == 0) //说明资源是存在的
            {
                if(S_ISDIR(st.st_mode))
                {
                    //表示请求的资源是一个目录,不被允许的,需要做一下相关处理
                    //虽然是一个目录,但是绝对不会以/结尾,上面已经做了处理
                    http_request.path += "/";
                    http_request.path += HOME_PAGE;
                    stat(http_request.path.c_str(),&st);
                }
                if( (st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH) )
                {
                    //表示请求的资源是可执行程序,这里需要做特殊处理
                    http_request.cgi = true;
                }
                http_response.size = st.st_size;
            }
            else
            {
                //说明资源不存在
                std::string info = http_request.path;
                info += " not found!";
                LOG(WARING,info);
                code = NOT_FOUND;
                goto END;
            }

            //对于get请求的资源进行特殊处理,获取文件的后缀名  用于组织响应对 Content-Type加文件扩展名
            found = http_request.path.rfind(".");
            if(found == std::string::npos)
            {
                //获取文件后缀名
                http_request.suffix = ".html";
            }
            else
            {
                http_request.suffix = http_request.path.substr(found);
            }

            //如果是cgi程序请求,则执行cgi相应处理
            if(http_request.cgi)
            {
                code = ProcessCgi();
            }
            else
            {
                //否则执行非cgi处理
                code = ProcessNonCgi();
            }
END:
            //组织http响应信息
            BuildHttpResponseHelper();
        }
        //发送响应信息
        void SendHttpResponse()
        {
            //发送响应首行
            send(sock,http_response.status_line.c_str(),http_response.status_line.size(),0);
            //发送响应头部信息
            for(auto iter : http_response.response_header)
            {
                send(sock,iter.c_str(),iter.size(),0);
            }
            //发送空行
            send(sock,http_response.blank.c_str(),http_response.blank.size(),0);
            //正文有两种形式: 文件fd cgi:response_body
            if(http_request.cgi)
            {
                //cgi程序的处理结果都放在response_body中
                auto& response_body = http_response.response_body;
                size_t size = 0;
                size_t total = 0;
                const char* start = response_body.c_str();
                while(total < response_body.size() && (size = send(sock,start+total,response_body.size()-total,0)) > 0)
                {
                    total += size;
                }
            }
            else
            {
                //非cgi请求以及错误响应,都是以文件的形式进行响应
                std::cout << "http_response.fd: " << http_response.fd << std::endl;
                std::cout << "http_response.size: " << http_response.size << std::endl;
                sendfile(sock,http_response.fd,nullptr,http_response.size);
                close(http_response.fd);
            }
        }
        ~EndPoint()
        {
            close(sock);
        }
};
//#define DEBUG 1
class CallBack{
    public:
        CallBack()
        {}
        //仿函数重载括号运算符:
        //CallBack对象(sock) -> 调用HandlerRequest函数
        void operator()(int sock)
        {
            HandlerRequest(sock);
        }

        //
        void HandlerRequest(int sock)
        {
            LOG(INFO,"hander request begin");
            
#ifdef DEBUG
            char buf[1024];
            recv(sock,buf,sizeof(buf),0);
            std::cout << "---------------begin-------------------" <<std::endl;
            std::cout << buf << std::endl;   
            std::cout << "----------------end---------------------" <<std::endl;
#else
            EndPoint* ep = new EndPoint(sock);
            //1.接收request请求，主要包含接收解析并且进行相应的业务逻辑处理:
            //首行：请求方法 URL 协议版本
            //头部信息：
            //空行：
            //正文数据：
            ep->RecvHttpRequest();
            if(!ep->IsStop()){
                LOG(INFO,"Recv No Error,Begin Build And Send");
                //构建对应的相应
                ep->BuildHttpResponse();
                //发送相应至浏览器
                ep->SendHttpResponse();
            }
            else
            {
                LOG(WARING,"Recv Error,Stop Build and Send");
            }
            delete ep;

#endif
            LOG(INFO,"hander request end");

        }

        ~CallBack()
        {}
};
